﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Mini_Project
{
    class RecommenderSystem
    {
        //class members here
        public Dictionary<double, List<pair>> userKey;              // user, item, rating
        public Dictionary<double, List<pair>> itemKey;              // item, user, rating

        public Dictionary<double, List<pair>> userData;           // user, item, rating
        public Dictionary<double, List<pair>> itemData;           // item, user, rating

        public Dictionary<double, List<pair>> trainUser;         // user, item, rating
        public Dictionary<double, List<pair>> trainItem;         // item, user, rating
        public Dictionary<double, List<pair>> testUser;          // user, item, rating
        public Dictionary<double, List<pair>> testItem;          // item, user, rating

        public Dictionary<double, List<pair>> trainUser_2;         // user, item, rating
        public Dictionary<double, List<pair>> trainItem_2;         // item, user, rating
        public Dictionary<double, List<pair>> testUser_2;          // user, item, rating
        public Dictionary<double, List<pair>> testItem_2; 

        public Dictionary<KeyValuePair<int, int>, double> wau_pearson;
        public Dictionary<KeyValuePair<int, int>, double> wau_cosine;
        public Dictionary<KeyValuePair<int, int>, double> wau_SVD;

        public Dictionary<KeyValuePair<int, int>, double> wau_pearson_train;
        public Dictionary<KeyValuePair<int, int>, double> wau_cosine_train;

        public Dictionary<KeyValuePair<int, int>, double> wau_pearson_test;
        public Dictionary<KeyValuePair<int, int>, double> wau_cosine_test;

        public Dictionary<KeyValuePair<int, int>, double> wau_pearson_all;
        public Dictionary<KeyValuePair<int, int>, double> wau_cosine_all;

        public Dictionary<String, Dictionary<double, int>> histograms = new Dictionary<String, Dictionary<double, int>>();

        public int totalLines = 0;

        public Dictionary<double, double> bu = null;
        public Dictionary<double, double> bi = null;
        public Dictionary<double, List<double>> pu = null;
        public Dictionary<double, List<double>> qi = null;

        public double u;
        
        public algorithms a;
        public helper h;

        public RecommenderSystem()
        {
            a = new algorithms(this);
            h = new helper(this);
            a.h = h;
            h.a = a;
        }

        public void Load(string sFileName)
        {
            userData = new Dictionary<double, List<pair>>();   // user, item, rating
            itemData = new Dictionary<double, List<pair>>();   // item, user, rating

            wau_pearson_all = new Dictionary<KeyValuePair<int, int>, double>();
            wau_cosine_all = new Dictionary<KeyValuePair<int, int>, double>();

            using (StreamReader sr = new StreamReader(sFileName))
            {
                String line;
                // Read data from u.data.
                while ((line = sr.ReadLine()) != null)  // go through all lines in database
                {
                    totalLines++;
                    char[] delimiters = new char[] { '\t' };
                    String[] parts = line.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
                    double[] doubleParts = new double[3];
                    for (int i = 0; i < 3; i++)
                    {
                        doubleParts[i] = Convert.ToDouble(parts[i]);
                    }
                    if (userData.ContainsKey(doubleParts[0]))
                    {
                        List<pair> temp = new List<pair>();
                        temp = userData[doubleParts[0]];
                        temp.Add(new pair(doubleParts[1], doubleParts[2]));
                        userData[doubleParts[0]] = temp;
                    }
                    else
                    {
                        List<pair> newEntry = new List<pair>();
                        newEntry.Add(new pair(doubleParts[1], doubleParts[2]));
                        userData.Add(doubleParts[0], newEntry);
                        user u = new user(doubleParts[0]);
                        h.users.Add(doubleParts[0], u);
                    }
                    if (itemData.ContainsKey(doubleParts[1]))
                    {
                        List<pair> temp = new List<pair>();
                        temp = itemData[doubleParts[1]];
                        temp.Add(new pair(doubleParts[0], doubleParts[2]));
                        itemData[doubleParts[1]] = temp;
                    }
                    else
                    {
                        List<pair> newEntry = new List<pair>();
                        newEntry.Add(new pair(doubleParts[0], doubleParts[2]));
                        itemData.Add(doubleParts[1], newEntry);
                        item i = new item(doubleParts[1]);
                        h.items.Add(doubleParts[1], i);
                    }
                }
            }


            // reading item genres from u.item
            using (StreamReader sr = new StreamReader("../../MovieLens/u.item"))
            {
                String line;
                double id;
                double[] genres;
                int first;
                item temp;
                while ((line = sr.ReadLine()) != null)  // go through all lines in file
                {
                    char[] delimiters = new char[] { '|' };
                    String[] parts = line.Split(delimiters, StringSplitOptions.RemoveEmptyEntries);
                    id = Convert.ToDouble(parts[0]);
                    first = parts.Length;
                    first -= 19;
                    genres = new double[19];
                    for (int i = 0; i < 19; i++)
                    {
                        genres[i] = Convert.ToDouble(parts[i+first]);
                        
                    }
                    temp = h.items[id];
                    temp.setGenres(genres);
                    h.items[id] = temp;
                }
            }
           
            // calculating user profile
            h.calcAllUserProfiles();
            
            userKey = userData;
            itemKey = itemData;

            wau_pearson = wau_pearson_all;
            wau_cosine = wau_cosine_all;
            wau_SVD = new Dictionary<KeyValuePair<int, int>, double>();

            h.find_all_items();
        }

        public void Load(string sFileName, double dTrainSetSize)
        {
            Load(sFileName);
            trainUser = new Dictionary<double, List<pair>>();     // user, item, rating
            testUser = new Dictionary<double, List<pair>>();      // user, item, rating
            trainItem = new Dictionary<double, List<pair>>();     // user, item, rating
            testItem = new Dictionary<double, List<pair>>();      // user, item, rating

            wau_cosine_train = new Dictionary<KeyValuePair<int, int>, double>();
            wau_pearson_train = new Dictionary<KeyValuePair<int, int>, double>();
            wau_cosine_test = new Dictionary<KeyValuePair<int, int>, double>();
            wau_pearson_test = new Dictionary<KeyValuePair<int, int>, double>();

            int amount = 0;
            Random r = new Random();
            while (amount < (totalLines - (totalLines * dTrainSetSize)))
            {
                int randUser = r.Next(1, (userKey.Count + 1));
                while ((!(userKey.ContainsKey(randUser))) || (trainUser.ContainsKey(randUser)))  // if this user doesnt exist,
                    randUser = ((randUser + 1) % (userKey.Count));                               // or was already chosen, pick a new one
                Random r1 = new Random();
                List<pair> ratings = userKey[randUser];
                int max = (ratings.Count) + 1;
                int randK = r1.Next(1, max);
                Random r2 = new Random();
                List<pair> addToTrain = new List<pair>();
                List<pair> addToItemTrain = new List<pair>();
                for (int j = 0; j < randK; j++)         // add k random ratings to the training set
                {
                    int itemIndex = r2.Next(1, max);                // index of item in ratings
                    itemIndex--;
                    while (addToTrain.Contains(ratings[itemIndex]))
                    {
                        itemIndex = ((itemIndex + 1) % (ratings.Count));
                    }
                    addToTrain.Add(ratings[itemIndex]);
                    double itemValue = ratings[itemIndex].getFirst();
                    if (trainItem.ContainsKey(itemValue))
                        addToItemTrain = trainItem[itemValue];
                    else
                    {
                        addToItemTrain = new List<pair>();
                    }
                    pair toAdd = new pair(randUser, (ratings[itemIndex].getSecond()));
                    addToItemTrain.Add(toAdd);
                    trainItem[itemValue] = addToItemTrain;  
                }
                trainUser.Add(randUser, addToTrain);
                List<pair> addToTest = new List<pair>();
                List<pair> addToItemTest = new List<pair>();
                if (randK < max)
                {
                    for (int i = 0; i < ratings.Count; i++)     // add the rest of the ratings to the testing set
                    {
                        if (!(addToTrain.Contains(ratings[i])))
                        {
                            addToTest.Add(ratings[i]);
                            double itemValue = ratings[i].getFirst();   // item number
                            if (testItem.ContainsKey(itemValue))
                                addToItemTest = testItem[itemValue];
                            else
                            {
                                addToItemTest = new List<pair>();
                            }
                            pair toAdd = new pair(randUser, (ratings[i].getSecond()));
                            addToItemTest.Add(toAdd);
                            if (testItem.ContainsKey(itemValue))
                                testItem[itemValue] = addToItemTest;
                            else
                                testItem.Add(itemValue, addToItemTest);
                        }
                    }
                    testUser.Add(randUser, addToTest);
                }
                amount = amount + (ratings.Count - randK);

            }
            for (int i = 0; i < userKey.Count; i++)         // add the rest of the users to the training set
            {
                List<pair> addToItemTrain = new List<pair>();
                if ((!(trainUser.ContainsKey(i))) && (!(testUser.ContainsKey(i))) && (userKey.ContainsKey(i)))
                {
                    trainUser.Add(i, userKey[i]);
                    List<pair> ratings = userKey[i];
                    for (int j = 0; j < ratings.Count; j++)
                    {
                        double itemValue = ratings[j].getFirst();   // item number
                        if (trainItem.ContainsKey(itemValue))
                            addToItemTrain = trainItem[itemValue];
                        else
                        {
                            addToItemTrain = new List<pair>();
                        }
                        pair toAdd = new pair(i, (ratings[j].getSecond()));
                        addToItemTrain.Add(toAdd);
                        trainItem[itemValue] = addToItemTrain;
                    }
                }
            }

            userKey = trainUser;
            itemKey = trainItem;

            totalLines = 0;

            foreach (KeyValuePair<double, List<pair>> rating in trainUser)
            {
                totalLines += rating.Value.Count;
            }

            double sum = 0;
            double total = 0;
            foreach (KeyValuePair<double, List<pair>> rating in itemKey)
            {
                foreach (pair user in rating.Value)
                {
                    sum += user.getSecond();
                }
                total += rating.Value.Count;
            }
            u = (sum / total);

            wau_pearson = wau_pearson_test;
            wau_cosine = wau_cosine_test;

            foreach (KeyValuePair<double, List<pair>> rating in testUser)
            {
                int totalRatings = 0;
                double ra = 0;
                Double sUID = rating.Key;
                Dictionary<double, int> dAllRatings_a = GetRatingsHistogram(sUID.ToString());
                foreach (KeyValuePair<double, int> p in dAllRatings_a)
                {
                    ra += (p.Key * p.Value);            // key - rating in stars, value - number of ratings with key stars
                    totalRatings += p.Value;
                }
                ra = (ra / totalRatings);               // ra - users average rating, weighted average

                foreach (KeyValuePair<double, List<pair>> user in testUser)
                {
                    if (user.Key != sUID)
                    {
                        a.calc_wau_pearson(sUID, user.Key, ra);
                        a.calc_wau_cosine(sUID, user.Key);
                    }
                }
            }
        }

        public Dictionary<double, int> GetRatingsHistogram(string sUID)
        {
            if (histograms.ContainsKey(sUID))
                return histograms[sUID];
            double UID = Convert.ToDouble(sUID);
            List<pair> userRatings = userKey[UID];
            Dictionary<double, int> ans = new Dictionary<double, int>();
            for (int i = 1; i < 6; i++)
            {
                ans.Add(i, 0);
            }
            for (int i = 0; i < userRatings.Count; i++)
            {
                ans[userRatings[i].getSecond()]++;
            }
            histograms.Add(sUID, ans);
            return ans;
        }



        public void TrainBaseModel(int cFeatures)
        {
            int i;
            double r_ui, iid, top, bottom;
            double ut, uv, y, l, RMSE_old = 100, RMSE_new = 100;
            bu = new Dictionary<double, double>();
            bi = new Dictionary<double, double>();
            pu = new Dictionary<double, List<double>>();
            qi = new Dictionary<double, List<double>>();
            Random random = new Random();

            h.trainTest(0.90);

            y = 0.05;
            l = 0.05;

            double sum = 0;
            double total = 0;
            foreach (KeyValuePair<double, List<pair>> rating in trainItem_2)
            {
                foreach (pair user in rating.Value)
                {
                    sum += user.getSecond();
                }
                total += rating.Value.Count;
            }
            ut = (sum / total);

            sum = 0;
            total = 0;
            foreach (KeyValuePair<double, List<pair>> rating in testItem_2)
            {
                foreach (pair user in rating.Value)
                {
                    sum += user.getSecond();
                }
                total += rating.Value.Count;
            }
            uv = (sum / total);

            foreach (KeyValuePair<double, List<pair>> user in userData)
            {
                bu.Add(user.Key, random.Next(-5, 5) * 0.01);
                pu.Add(user.Key, new List<double>());
                for (i = 0; i < cFeatures; i++)
                {
                    pu[user.Key].Add(random.Next(-5, 5) * 0.01);
                }
            }
            foreach (KeyValuePair<double, List<pair>> item in itemData)
            {
                bi.Add(item.Key, random.Next(-5, 5) * 0.01);
                qi.Add(item.Key, new List<double>());
                for (i = 0; i < cFeatures; i++)
                {
                    qi[item.Key].Add(random.Next(-5, 5) * 0.01);
                }
            }

            while (RMSE_old - RMSE_new >= 0)
            {
                top = 0;
                bottom = 0;
                double puqi = 0;
                foreach (KeyValuePair<double, List<pair>> rating in trainItem_2)
                {
                    iid = rating.Key;
                    foreach (pair user in rating.Value)
                    {
                        a.trainSVD(user.getFirst(), iid, user.getSecond(), ut, y, l);
                    }
                }
                foreach (KeyValuePair<double, List<pair>> rating in testItem_2)
                {
                    iid = rating.Key;
                    foreach (pair user in rating.Value)
                    {
                        for (i = 0; i < cFeatures; i++)
                        {
                            puqi += (pu[user.getFirst()][i] * qi[iid][i]);
                        }

                        r_ui = uv + bi[iid] + bu[user.getFirst()] + puqi;
                        top += Math.Pow((user.getSecond() - r_ui), 2);
                    }
                    bottom += rating.Value.Count;
                }
                RMSE_old = RMSE_new;
                RMSE_new = Math.Sqrt(top / bottom);
            }
        }

        public double GetRating(string sUID, string sIID)
        {
            double UID = Convert.ToDouble(sUID);
            double IID = Convert.ToDouble(sIID);
            List<pair> userRatings = userKey[UID];
            pair ans = userRatings.Find(
                delegate(pair pr)
                {
                    return pr.getFirst() == IID;
                }
            );
            if (ans == null)
                return 0;
            return ans.getSecond();
        }

        public List<string> Recommend(string sAlgorithm, string sUserId, int cRecommendations, int start)
        {
            int recommendations = cRecommendations + start;
            List<string> recs = new List<string>();

            if (userKey.ContainsKey(Convert.ToDouble(sUserId)))
            {
                if (sAlgorithm.CompareTo("Popularity") == 0)
                {
                    recs = h.RecommendPopularity(sUserId, recommendations);
                }
                else if (sAlgorithm.CompareTo("Cosine") == 0 || sAlgorithm.CompareTo("SVD") == 0 || sAlgorithm.CompareTo("Pearson") == 0)
                {
                    recs = h.RecommendPredictions(sAlgorithm, sUserId, recommendations);
                }
                else if (sAlgorithm.CompareTo("NNCosine") == 0 || sAlgorithm.CompareTo("NNSVD") == 0 || sAlgorithm.CompareTo("NNPearson") == 0)
                {
                    recs = h.RecommendNN(sAlgorithm, sUserId, recommendations);
                }
                else if (sAlgorithm.CompareTo("CP") == 0)
                {
                    recs = h.RecommendCP(sUserId, recommendations);
                }

                for (int i = 0; recs.Count() > 0 && i < start; i++)
                {
                    recs.RemoveAt(0);
                }

                return recs;
            }
            else
            {
                Console.WriteLine("user requested is not in training set");
                return null;
            }
        }

        public double PredictRating(string sMethod, string sUID, string sIID)
        {
            if ((sMethod == "Pearson") || (sMethod == "Cosine"))
            {
                return a.PredictRAU(sUID, sIID, sMethod);
            }
            else if (sMethod == "Random")
            {
                return a.randomPredict(sUID);
            }
            else if (sMethod == "SVD")
            {
                if (bu != null)
                {
                    double puqi = 0;
                    double iid = Convert.ToDouble(sIID);
                    double uid = Convert.ToDouble(sUID);
                    for (int i = 0; i < pu[uid].Count; i++)
                    {
                        puqi += (pu[uid][i] * qi[iid][i]);
                    }

                    return (u + bi[iid] + bu[uid] + puqi);
                }
                return u;
            }
            else
            {
                return u;
            }
        }

        public List<string> NoveltyRecommend(string sAlgorithm, string sUserId, int cRecommendations, double novelty_percent, double static_percent)
        {
            int novelty = (int)(cRecommendations*novelty_percent);
            int total_similar = cRecommendations - novelty;
            int similar_added = 0;
            int start = 0;
            double distance;
            List<string> AllRecommendations;
            List<string> recommendations = new List<string>();
            int nof_recs = (int)(cRecommendations * 8);
            int past_count = 1;

            while (recommendations.Count() < cRecommendations && (past_count > 0))
            {

                AllRecommendations = Recommend(sAlgorithm, sUserId, nof_recs, start);
                foreach (string item in AllRecommendations)
                {
                    if (recommendations.Count() < cRecommendations)
                    {
                        distance = h.NoveltyDistance(h.items[Convert.ToDouble(item)], h.users[Convert.ToDouble(sUserId)].profile);

                        // similar item
                        if (distance < static_percent && similar_added < total_similar)
                        {
                            recommendations.Add(item);
                            similar_added++;
                        }
                        // novelty item
                        else if (distance >= static_percent)
                        {
                            recommendations.Add(item);
                        }
                    }
                    else
                    {
                        break;
                    }
                }
                start = nof_recs + 1;
                nof_recs *= 2;

                past_count = AllRecommendations.Count();
            }

            if (recommendations.Count() == past_count)
            {
                Console.WriteLine("Could not find enough recommendations, novelty_percent: " + novelty_percent + " double static_percent: " + static_percent);
            }

            return recommendations;
        }


        //results: length -> algo -> precision -> recall
        //run our new method on all algos, with static precentages, and various lengths
        public Dictionary<int, Dictionary<string, Dictionary<string, double>>> ComputeDifferentLengths(List<String> lMethods, List<int> lLengths, double noveltyPrecent, double simThreshold)
        {
            Dictionary<int, Dictionary<string, Dictionary<string, double>>> ans = new Dictionary<int, Dictionary<string, Dictionary<string, double>>>();
            Dictionary<string, double> metric_dic;
            Dictionary<string, Dictionary<string, double>> method_dic;
            int max_length, totalUsers = testUser.Count();
            string uid;
            List<string> recommendations;
            List<string> userRatings;
            IEnumerable<string> diff, recs;
            Dictionary<int, double> fp, fn, tp;
            Dictionary<int, double> precision, recall;

            foreach (int length in lLengths)
            {
                method_dic = new Dictionary<string, Dictionary<string, double>>();
                ans.Add(length, method_dic);
            }

            max_length = lLengths.Max();
            foreach (string method in lMethods)
            {
                fp = new Dictionary<int, double>();
                fn = new Dictionary<int, double>();
                tp = new Dictionary<int, double>();
                precision = new Dictionary<int, double>();
                recall = new Dictionary<int, double>();
                foreach (int length in lLengths)
                {
                    tp.Add(length, 0);
                    fp.Add(length, 0);
                    fn.Add(length, 0);
                    precision.Add(length, 0);
                    recall.Add(length, 0);
                }
                foreach (KeyValuePair<double, List<pair>> user in testUser)
                {
                    uid = user.Key.ToString();

                    Console.WriteLine("noveltyPercent: " + noveltyPrecent + " uid: " + uid);

                    recommendations = NoveltyRecommend(method, uid, max_length, noveltyPrecent, simThreshold);
                    userRatings = new List<string>();
                    foreach (pair rate in user.Value)
                    {
                        userRatings.Add(rate.getFirst().ToString());
                    }

                    foreach (int length in lLengths)
                    {
                        tp[length] = 0;
                        fp[length] = 0;
                        fn[length] = 0;
                        recs = recommendations.Take(length);
                        foreach (pair rate in user.Value)
                        {
                            if (recs.Contains(rate.getFirst().ToString()))
                            {
                                tp[length] = tp[length] + 1;
                            }
                            else
                            {
                                fn[length] = fn[length] + 1;
                            }
                        }
                        diff = recs.Except(userRatings);
                        fp[length] = fp[length] + diff.Count();

                        precision[length] = precision[length] + (tp[length] / (tp[length] + fp[length]));
                        recall[length] = recall[length] + (tp[length] / (tp[length] + fn[length]));
                    }
                }
                foreach (int length in lLengths)
                {
                    precision[length] = precision[length] / totalUsers;
                    recall[length] = recall[length] / totalUsers;
                    metric_dic = new Dictionary<string, double>();
                    metric_dic.Add("Precision", precision[length]);
                    metric_dic.Add("Recall", recall[length]);

                    method_dic = ans[length];
                    method_dic.Add(method, metric_dic);
                    ans[length] = method_dic;
                }
            }
            return ans;
        }

        //results: threshold -> algo -> precision -> recall
        // calculates for a given novelty percent, and a given length, for every threshold and every algo -> precision and recall
        public Dictionary<double, Dictionary<string, Dictionary<string, double>>> ComputeDifferentThreshold(List<String> lMethods, List<double> simThreshold, double noveltyPrecent, int lengthy)
        {
            Dictionary<double, Dictionary<string, Dictionary<string, double>>> ans = new Dictionary<double, Dictionary<string, Dictionary<string, double>>>();
            Dictionary<string, double> metric_dic;
            Dictionary<string, Dictionary<string, double>> method_dic;
            int totalUsers = testUser.Count();
            string uid;
            List<string> recommendations;
            List<string> userRatings;
            IEnumerable<string> diff;
            Dictionary<double, double> fp, fn, tp;
            Dictionary<double, double> precision, recall;

            foreach (double thresh in simThreshold)
            {
                method_dic = new Dictionary<string, Dictionary<string, double>>();
                ans.Add(thresh, method_dic);
            }

            foreach (string method in lMethods)
            {
                fp = new Dictionary<double, double>();
                fn = new Dictionary<double, double>();
                tp = new Dictionary<double, double>();
                precision = new Dictionary<double, double>();
                recall = new Dictionary<double, double>();
                foreach (double thresh in simThreshold)
                {
                    tp.Add(thresh, 0);
                    fp.Add(thresh, 0);
                    fn.Add(thresh, 0);
                    precision.Add(thresh, 0);
                    recall.Add(thresh, 0);
                }
                foreach (KeyValuePair<double, List<pair>> user in testUser)
                {
                    uid = user.Key.ToString();

                    Console.WriteLine("noveltyPercent: " + noveltyPrecent + " uid: " + uid);

                    userRatings = new List<string>();
                    foreach (pair rate in user.Value)
                    {
                        userRatings.Add(rate.getFirst().ToString());
                    }

                    foreach (double thresh in simThreshold)
                    {
                        recommendations = NoveltyRecommend(method, uid, lengthy, noveltyPrecent, thresh);
                        tp[thresh] = 0;
                        fp[thresh] = 0;
                        fn[thresh] = 0;
                        foreach (pair rate in user.Value)
                        {
                            if (recommendations.Contains(rate.getFirst().ToString()))
                            {
                                tp[thresh] = tp[thresh] + 1;
                            }
                            else
                            {
                                fn[thresh] = fn[thresh] + 1;
                            }
                        }
                        diff = recommendations.Except(userRatings);
                        fp[thresh] = fp[thresh] + diff.Count();

                        precision[thresh] = precision[thresh] + (tp[thresh] / (tp[thresh] + fp[thresh]));
                        recall[thresh] = recall[thresh] + (tp[thresh] / (tp[thresh] + fn[thresh]));
                    }
                }
                foreach (double thresh in simThreshold)
                {
                    precision[thresh] = precision[thresh] / totalUsers;
                    recall[thresh] = recall[thresh] / totalUsers;
                    metric_dic = new Dictionary<string, double>();
                    metric_dic.Add("Precision", precision[thresh]);
                    metric_dic.Add("Recall", recall[thresh]);

                    method_dic = ans[thresh];
                    method_dic.Add(method, metric_dic);
                    ans[thresh] = method_dic;
                }
            }
            return ans;
        }

        //results: novelty percentage -> algo -> precision -> recall
        // calculates for a given similarity threshold, and a given length, for every novelty percentage and every algo -> precision and recall
        public Dictionary<double, Dictionary<string, Dictionary<string, double>>> ComputeDifferentNoveltyPercent(List<String> lMethods, List<double> novPrecent, double similatiryThreshold, int lengthy)
        {
            Dictionary<double, Dictionary<string, Dictionary<string, double>>> ans = new Dictionary<double, Dictionary<string, Dictionary<string, double>>>();
            Dictionary<string, double> metric_dic;
            Dictionary<string, Dictionary<string, double>> method_dic;
            int totalUsers = testUser.Count();
            string uid;
            List<string> recommendations;
            List<string> userRatings;
            IEnumerable<string> diff;
            Dictionary<double, double> fp, fn, tp;
            Dictionary<double, double> precision, recall;

            int u_count;

            foreach (double thresh in novPrecent)
            {
                method_dic = new Dictionary<string, Dictionary<string, double>>();
                ans.Add(thresh, method_dic);
            }

            foreach (string method in lMethods)
            {
                fp = new Dictionary<double, double>();
                fn = new Dictionary<double, double>();
                tp = new Dictionary<double, double>();
                precision = new Dictionary<double, double>();
                recall = new Dictionary<double, double>();
                foreach (double thresh in novPrecent)
                {
                    tp.Add(thresh, 0);
                    fp.Add(thresh, 0);
                    fn.Add(thresh, 0);
                    precision.Add(thresh, 0);
                    recall.Add(thresh, 0);
                }
                u_count = 0;
                foreach (KeyValuePair<double, List<pair>> user in testUser)
                {
                    if (u_count < 200)
                    {
                        uid = user.Key.ToString();

                        Console.WriteLine("Similarity Threshold: " + similatiryThreshold + " uid: " + uid);

                        userRatings = new List<string>();
                        foreach (pair rate in user.Value)
                        {
                            userRatings.Add(rate.getFirst().ToString());
                        }

                        foreach (double thresh in novPrecent)
                        {
                            recommendations = NoveltyRecommend(method, uid, lengthy, thresh, similatiryThreshold);
                            tp[thresh] = 0;
                            fp[thresh] = 0;
                            fn[thresh] = 0;
                            foreach (pair rate in user.Value)
                            {
                                if (recommendations.Contains(rate.getFirst().ToString()))
                                {
                                    tp[thresh] = tp[thresh] + 1;
                                }
                                else
                                {
                                    fn[thresh] = fn[thresh] + 1;
                                }
                            }
                            diff = recommendations.Except(userRatings);
                            fp[thresh] = fp[thresh] + diff.Count();

                            precision[thresh] = precision[thresh] + (tp[thresh] / (tp[thresh] + fp[thresh]));
                            recall[thresh] = recall[thresh] + (tp[thresh] / (tp[thresh] + fn[thresh]));
                        }
                        u_count++;
                    }
                }
                foreach (double thresh in novPrecent)
                {
                    precision[thresh] = precision[thresh] / totalUsers;
                    recall[thresh] = recall[thresh] / totalUsers;
                    metric_dic = new Dictionary<string, double>();
                    metric_dic.Add("Precision", precision[thresh]);
                    metric_dic.Add("Recall", recall[thresh]);

                    method_dic = ans[thresh];
                    method_dic.Add(method, metric_dic);
                    ans[thresh] = method_dic;
                }
            }
            return ans;
        }

        public void DisplayInExcel(Dictionary<int, Dictionary<string, Dictionary<string, double>>> results)
        {
            h.GraphDifferentLengths(results);
        }

        public void DisplayInExcelPrecent(Dictionary<double, Dictionary<string, Dictionary<string, double>>> results, String param)
        {
            h.GraphDifferentParams(results, param);
        }
    }
}
